package com.huawei.spare.part.replace.framework;

import com.google.common.base.Stopwatch;
import com.google.common.collect.Lists;
import com.huawei.spare.part.replace.views.dashboard.DashboardPresenter;
import com.huawei.spare.part.replace.views.home.HomeView;
import com.huawei.spare.part.replace.views.prepare.defective.DefectiveView;
import com.huawei.spare.part.replace.views.prepare.download.DownloadView;
import com.huawei.spare.part.replace.views.prepare.project.ProjectView;
import com.huawei.spare.part.replace.views.prepare.risk.RiskView;
import com.huawei.spare.part.replace.views.prepare.spare.SpareView;
import com.huawei.spare.part.replace.views.prepare.updatedb.UpdateDbView;
import com.huawei.spare.part.replace.views.replace.confirm.ConfirmView;
import com.huawei.spare.part.replace.views.replace.load.LoadView;
import com.huawei.spare.part.replace.views.replace.perform.PerformView;
import com.huawei.spare.part.replace.views.replace.result.ResultView;
import com.huawei.spare.part.replace.views.replace.slot.SlotView;
import com.huawei.spare.part.replace.views.replace.smm.SmmView;
import com.huawei.spare.part.replace.views.replace.switch0.SwitchView;
import com.huawei.spare.part.replace.views.replace.upgrade.UpgradeView;
import com.huawei.spare.part.replace.views.replace.uplink.UplinkView;
import io.reactivex.rxjavafx.observables.JavaFxObservable;
import java.util.List;
import java.util.Optional;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public enum SmartRouter {

  INSTANCE;

  private static final Logger log = LoggerFactory.getLogger(SmartRouter.class);

  private boolean init = false;
  private Menu activeMenu;
  private DashboardPresenter presenter;
  ObservableList<SmartContentView> contentViewStack;

  public static SmartRouter get() {
    if (SmartRouter.INSTANCE.init) {
      return SmartRouter.INSTANCE;
    }
    throw new RuntimeException("SmartRouter must be initialized before using");
  }

  public static void initialize(DashboardPresenter dashboardPresenter) {
    INSTANCE.presenter = dashboardPresenter;
    INSTANCE.contentViewStack = FXCollections.observableArrayList();
    // observe content view stack
    JavaFxObservable.emitOnChanged(INSTANCE.contentViewStack)
        .subscribe(smartContentViews -> {
          INSTANCE.renderFooterView();
          INSTANCE.renderTitleView();
        });
    INSTANCE.init = true;
  }

  /**
   * Route to a new Menu
   */
  public void route(Menu menu) {
    if (!menu.equals(activeMenu)) { // route to new menu
      Stopwatch watch = Stopwatch.createStarted();

      getCurrentView().ifPresent((view) -> view.getPresenter().dismissLoading());

      // toggle menu
      activeMenu = menu;
      presenter.getMenu().getSelectionModel().select(menu);

      // clear view when toggle menu
      getContentContainer().getChildren().clear();
      onViewStackCleared();
      contentViewStack.clear();

      // add view to content view stack
      SmartContentView view = getInstance(menu.getViews().get(0));
      nextStepButton().disableProperty().unbind();
      nextStepButton().setDisable(false);
      renderContentView(view);
      final Stopwatch stop = watch.stop();
      log.info("Route to menu {}, cost {}", menu, stop);
    }
  }

  /**
   * current active view
   */
  private Optional<SmartContentView> getCurrentView() {
    if (contentViewStack != null && contentViewStack.size() > 0) {
      return Optional.of(contentViewStack.get(contentViewStack.size() - 1));
    }
    return Optional.empty();
  }

  /**
   * route to next step view for stack-pane.
   */
  public void routeToNextView() {
    final SmartContentView currentContentView = contentViewStack.get(contentViewStack.size() - 1);
    final SmartContentView currentView = getCurrentView().get();
    if (currentView instanceof ProjectView) {
      SmartDataBus.get().setAutoLoad(true);   // auto load last project saved file
      route(Menu.Apply);
    } else if (currentContentView != null && activeMenu.getViews().size() > contentViewStack.size()) {
      currentContentView.getPresenter().onFinish().subscribe((result) -> {
        if (result) {
          // dismiss loading if exists
          currentContentView.getPresenter().dismissLoading();
          Stopwatch watch = Stopwatch.createStarted();
          Class<? extends SmartContentView> viewClazz = activeMenu.getViews().get(contentViewStack.size());
          renderContentView(getInstance(viewClazz));
          final int size = contentViewStack.size();
          final String viewName = contentViewStack.get(size - 1).getClass().getSimpleName();
          log.debug("Route to next step  costs {}, current view {}, stack size: {}", watch.stop(), viewName, size);
        }
      });
    }
  }

  /**
   * route to previous step view for stack-pane.
   */
  public void routeToPreviousView() {
    if (contentViewStack.size() > 1) {
      Optional<SmartContentView> currentView = getCurrentView();
      final SmartContentView view = currentView.get();
      currentView.ifPresent((v) -> {
        view.getPresenter().onLeave().subscribe((result) -> {
          if (result) {
            contentViewStack.remove(view);
            view.getPresenter().dismissLoading();
            nextStepButton().disableProperty().unbind();
            nextStepButton().setDisable(false);
            getContentContainer().getChildren().remove(view.getView());
            final int size = contentViewStack.size();
            final String viewName = contentViewStack.get(size - 1).getClass().getSimpleName();
            log.debug("Route to previous step finished, current view {}, stack size: {}", viewName, size);
          }
        });
      });
    }
  }

  /**
   * render footer view
   */
  private void renderFooterView() {
    if (activeMenu != null && contentViewStack != null && contentViewStack.size() != 0) {
      // no footer bar required when the menu only has one content view
      boolean singleContentView = activeMenu.getViews().size() == 1;
      presenter.getFooter().setVisible(!singleContentView);
      presenter.getFooter().setManaged(!singleContentView);
      presenter.getFooter().setDisable(false);

      presenter.getPreviousStep().setVisible(contentViewStack.size() != 1);
      presenter.getPreviousStep().setManaged(contentViewStack.size() != 1);
      presenter.getPreviousStep().setDisable(false);

      final SmartContentView currentView = getCurrentView().get();
      if (currentView instanceof ProjectView) {
        presenter.getNextStep().setVisible(true);
        presenter.getNextStep().setManaged(true);
        presenter.getNextStep().setDisable(false);
      } else {
        presenter.getNextStep().setVisible(contentViewStack.size() < activeMenu.getViews().size());
        presenter.getNextStep().setManaged(contentViewStack.size() < activeMenu.getViews().size());
        presenter.getNextStep().setDisable(false);
      }
    }
  }

  public void disableFooterView(boolean disabled) {
    presenter.getFooter().setDisable(disabled);
  }

  public Button nextStepButton() {
    return presenter.getNextStep();
  }

  /**
   * render content view
   */
  private void renderContentView(SmartContentView view) {
    nextStepButton().setDisable(false);
    contentViewStack.add(view);
    view.getView().requestLayout();
    getContentContainer().getChildren().add(view.getView());
  }

  /**
   * Render title view, every content view will return a special title
   */
  private void renderTitleView() {
    if (!CollectionUtils.isEmpty(contentViewStack) && contentViewStack.size() > 0) {
      presenter.getTitle().setText(contentViewStack.get(contentViewStack.size() - 1).getTitle());
    }
  }

  private StackPane getContentContainer() {
    return presenter.getContent();
  }

  private SmartContentView getInstance(Class<? extends SmartContentView> viewClazz) {
    try {
      return viewClazz.newInstance();
    } catch (InstantiationException | IllegalAccessException e) {
      String msg = "Could not construct view: " + viewClazz.getSimpleName();
      log.error(msg, e);
      throw new RuntimeException(msg, e);
    }
  }

  /**
   * 退出时释放资源
   */
  public void onViewStackCleared() {
    if (contentViewStack != null) {
      while(contentViewStack.size() > 0) {
        final SmartContentView remove = contentViewStack.remove(contentViewStack.size() - 1);
        remove.getPresenter().onLeave();
      }
    }
  }

  /**
   * All menu enum define
   */
  public enum Menu {

    Home(0, "nav.menu.welcome", Lists.newArrayList(HomeView.class)),
    Prepare(1, "nav.menu.prepare", Lists.newArrayList(UpdateDbView.class, DefectiveView.class,
        SpareView.class, RiskView.class, DownloadView.class, ProjectView.class)),
    Apply(2, "nav.menu.apply", Lists.newArrayList(LoadView.class, ConfirmView.class, SmmView.class,
        SwitchView.class, SlotView.class, UplinkView.class, PerformView.class, UpgradeView.class, ResultView.class)),
    Exception(3, "nav.menu.exception", Lists.newArrayList(PerformView.class));

    final int idx;
    final String displayKey;
    final List<Class<? extends SmartContentView>> views;

    Menu(int idx, String displayKey, List<Class<? extends SmartContentView>> views) {
      this.idx = idx;
      this.displayKey = displayKey;
      this.views = views;
    }

    public int getIdx() {
      return idx;
    }

    public String getDisplayKey() {
      return displayKey;
    }

    public List<Class<? extends SmartContentView>> getViews() {
      return views;
    }

    @Override
    public String toString() {
      return SmartDataBus.getString(displayKey);
    }
  }
}
